﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace Server
{
	internal enum ObjectType : byte
	{
		A,
		B,
		C
	}

	public interface ISerializable
	{
		byte[] Serialize();
		ISerializable Deserialize(byte[] source);
		void Execute();
	}

	public class A : ISerializable
	{
		private readonly int _value;

		public A()
		{
		}

		public A(int value)
		{
			_value = value;
		}

		public int Value
		{
			get { return _value; }
		}

		#region Implementation of ISerializable

		public byte[] Serialize()
		{
			var stream = new MemoryStream(sizeof (byte) + sizeof (int));
			stream.WriteByte((byte) ObjectType.A);
			byte[] data = BitConverter.GetBytes(_value);
			stream.Write(data, 0, data.Length);
			return stream.GetBuffer();
		}

		public ISerializable Deserialize(byte[] source)
		{
			var stream = new MemoryStream(source);
			var type = (ObjectType) stream.ReadByte();
			if (type != ObjectType.A)
			{
				return null;
			}
			var data = new byte[sizeof (int)];
			stream.Read(data, 0, sizeof (int));
			var result = new A(BitConverter.ToInt32(data, 0));
			return result;
		}

		public void Execute()
		{
		}

		#endregion
	}

	public class B : ISerializable
	{
		private readonly char _value = '\0';

		public B()
		{
		}

		public B(char value)
		{
			_value = value;
		}

		public char Value
		{
			get { return _value; }
		}

		#region Implementation of ISerializable

		public byte[] Serialize()
		{
			var stream = new MemoryStream(sizeof (byte) + sizeof (char));
			stream.WriteByte((byte) ObjectType.B);
			byte[] data = BitConverter.GetBytes(_value);
			stream.Write(data, 0, data.Length);
			return stream.GetBuffer();
		}

		public ISerializable Deserialize(byte[] source)
		{
			var stream = new MemoryStream(source);
			var type = (ObjectType) stream.ReadByte();
			if (type != ObjectType.B)
			{
				return null;
			}
			var data = new byte[sizeof (char)];
			stream.Read(data, 0, sizeof (char));
			var result = new B(BitConverter.ToChar(data, 0));
			return result;
		}

		public void Execute()
		{
		}

		#endregion
	}

	public class C : ISerializable
	{
		private readonly double _value;

		public C()
		{
		}

		public C(double value)
		{
			_value = value;
		}

		#region Implementation of ISerializable

		public byte[] Serialize()
		{
			var stream = new MemoryStream(sizeof (byte) + sizeof (double));
			stream.WriteByte((byte) ObjectType.C);
			byte[] data = BitConverter.GetBytes(_value);
			stream.Write(data, 0, data.Length);
			return stream.GetBuffer();
		}

		public ISerializable Deserialize(byte[] source)
		{
			var stream = new MemoryStream(source);
			var type = (ObjectType) stream.ReadByte();
			if (type != ObjectType.C)
			{
				return null;
			}
			var data = new byte[sizeof (double)];
			stream.Read(data, 0, sizeof (double));
			var result = new C(BitConverter.ToDouble(data, 0));
			return result;
		}

		public void Execute()
		{
			Console.WriteLine("Executing C instance... my value is {0}", _value);
		}

		#endregion
	}

	internal class Program
	{
		private static void Main(string[] args)
		{
			var listener = new TcpListener(new IPEndPoint(IPAddress.Any, 15000));
			listener.Start();
			TcpClient client = listener.AcceptTcpClient();
			NetworkStream stream = client.GetStream();
			for (int i = 0; i < 5; ++i)
			{
				var a = new A(i*i);
				byte[] aData = a.Serialize();
				stream.Write(BitConverter.GetBytes(aData.Length), 0, sizeof (int));
				stream.Write(aData, 0, aData.Length);

				var b = new B((char) (i + 70));
				byte[] bData = b.Serialize();
				stream.Write(BitConverter.GetBytes(bData.Length), 0, sizeof (int));
				stream.Write(bData, 0, bData.Length);

				var c = new C(i/2.0);
				byte[] cData = c.Serialize();
				stream.Write(BitConverter.GetBytes(cData.Length), 0, sizeof (int));
				stream.Write(cData, 0, cData.Length);
			}
			client.Close();
			listener.Stop();
		}
	}
}